/*
 * @Author: wwssaabb
 * @Date: 2021-11-06 15:56:10
 * @LastEditTime: 2021-12-17 11:24:03
 * @FilePath: \handwritten-code\promise2.js
 */

//手写promise 
//实现 new promise 、then 、 race 、all

//三个常量状态
const PENDING = 'pending' //等待
const FULFILLED = 'fulfilled' //完成
const REJECTED = 'rejected' //失败

class MyPromise {
  constructor(executor) {
    //executor为立即执行器函数
    //捕获错误
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  //存储静态状态，默认值为pending
  status = PENDING
  //value完成之后的值
  value = null
  //失败的原因
  reason = null
  //成功时的回调队列
  onFulfilledCallback = []
  //失败时的回调队列
  onRejectedCallback = []

  //resolve静态方法
  static resolve(parameter) {
    //如果是promise实例则返回
    if (parameter instanceof MyPromise) {
      return parameter
    }
    //普通值转成调用resolve方法
    return new MyPromise(resolve => resolve(parameter))
  }

  //reject静态方法
  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason))
  }


  //改变成功状态的函数,通过箭头函数将this指向实例
  resolve = (value) => {
    //只有状态是等待时才会改变
    if (this.status === PENDING) {
      //将状态改变为完成
      this.status = FULFILLED
      //存储成功的值
      this.value = value
      //循环取出队列里的回调进行执行
      while (this.onFulfilledCallback.length) {
        this.onFulfilledCallback.shift()(value)
      }
    }

  }
  //改变失败状态的函数,通过箭头函数将this指向实例
  reject = (reason) => {
    //只有等待的状态才会改变
    if (this.status === PENDING) {
      //改变为失败的状态
      this.status = REJECTED
      //存储失败的原因
      this.reason = reason
      //循环取出队列里的回调进行执行
      while (this.onRejectedCallback.length) {
        this.onRejectedCallback.shift()(reason)
      }
    }
  }

  //.then回调
  then(onFulfilled, onRejected) {

    //如果不传则使用默认函数
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {
      throw reason
    }

    const self = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        queueMicrotask(() => {
          //执行时错误捕获，改变状态并返回错误
          try {
            resolvePromise(realOnFulfilled(this.value), self, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          //执行时错误捕获，改变状态并返回错误
          try {
            resolvePromise(realOnRejected(this.reason), self, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      if (this.status === FULFILLED) {
        //成功时如果成功回调的结果也是promise的情况，例如链式调用
        //利用queueMicrotask创建一个微任务等待self初始化
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        //利用queueMicrotask创建一个微任务等待self初始化
        rejectedMicrotask()
      } else if (this.status === PENDING) {
        //如果状态是pending则将回调存储
        this.onFulfilledCallback.push(fulfilledMicrotask)
        this.onRejectedCallback.push(rejectedMicrotask)
      }
    })
    return self
  }

  catch (onRejected) {
    //catch只需要进行错误处理
    this.then(undefined, onRejected)
  }

  // finally() {

  // }
}

function resolvePromise(instance, self, resolve, reject) {
  //如果是自己则抛出类型错误
  if (instance === self) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }

  if (typeof instance === 'object' || typeof instance === 'function') {
    if (instance === null) return resolve(instance)

    let then
    try {
      then = instance.then
    } catch (error) {
      return reject(error)
    }

    if (typeof then === 'function') {
      let called = false
      try {
        then.call(instance, x => {
          if (called) return
          called = true
          resolvePromise(instance, x, resolve, reject)
        }, y => {
          if (called) return
          called = true
          reject(y)
        })
      } catch (error) {
        if (called) return
        reject(error)
      }
    } else {
      resolve(instance)
    }
  } else {
    resolve(instance)
  }

}

module.exports = MyPromise

MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}

module.exports = MyPromise;